import numpy as np  # 导入numpy包并将其命名为np
from typing import Literal

class TOPSIS:
    def __init__(self, array):
        """
        相关信息的传入和准备
        """
        self.array = array
        self.n = array.shape[0] #方案数
        self.m = array.shape[1] #指标数
        self.std_S = None #标准化后所有方案的得分
        
    def data_positivization(self, type: Literal['micro', 'intermediate', 'interval'], i, reference1 = None, reference2 = None):
        '''
        功能：对指标进行正向化处理
        参数：type：指标类型（1：micro极小型，2：intermediate中间型，3：interval区间型）
              i：正在处理的是原始矩阵的哪一列
        返回：正向化处理后的数据矩阵
        '''
        if type == 'micro':  #极小型
            print("第",i,"列是极小型，正向化中...")
            assert reference1 is None and reference2 is None, "极小型不需要额外参数"
            self.array[:, i] = np.max(self.array[:, i])- self.array[:, i]
            return print("第",i,"列极小型处理完成")
        elif type == 'intermediate':  #中间型
            print("第",i,"列是中间型")
            assert reference1 is not None, "中间型指标需要提供参考值"
            m = np.max(abs(self.array[:, i] - reference1))
            self.array[:, i] = 1 - abs(self.array[:, i] - reference1)/m
            return print("第",i,"列中间型处理完成")
        elif type == 'interval':  #区间型
            print("第",i,"列是区间型")
            assert reference1 is not None and reference2 is not None, "区间型指标需要提供两个参考值"
            assert reference1 < reference2, "区间型指标的两个参考值必须满足reference1 < reference2"
            m = np.max((abs(reference1 - np.min(self.array[:, i])), abs(reference2 - np.max(self.array[:, i]))))
            for r in range(self.n):
                if self.array[r, i] < reference1:
                    self.array[r, i] = 1-(reference1-self.array[r, i]) / m
                elif self.array[r, i] > reference2:
                    self.array[r, i] = 1-(self.array[r, i] - reference2) / m
                else:
                    self.array[r, i] = 1
            print("第",i,"列区间型处理完成")
            return print("第", i, "列区间型处理完成")
        
    def normalization(self):
        '''
        功能：对正向化后的矩阵进行标准化处理
        '''
        print("标准化处理中...")
        temp1 = (self.array * self.array).sum(axis=0)
        temp2 = np.tile(temp1, (self.n, 1))  # 将矩阵temp1平铺n行
        self.array = self.array / (temp2 ** 0.5)
        return("标准化处理完成")

    def cal_score(self, weight: list = []):
        '''
        功能：基于TOPSIS模型求解每个案例的得分，支持加权欧氏距离
        参数：weight 权重列表，长度等于指标数
        '''
        print(self.m)
        if weight and len(weight) == self.m:
            weight = np.array(weight)
            assert np.sum(weight)==1,"指标权重之和应为1"
            # 加权标准化矩阵
            weighted_array = self.array * weight
            temp_max = np.max(weighted_array, axis=0)  # PIS
            temp_min = np.min(weighted_array, axis=0)  # NIS
            temp1 = weighted_array - np.tile(temp_max, (self.n, 1))
            temp2 = weighted_array - np.tile(temp_min, (self.n, 1))
            D_P = np.sum((temp1 ** 2), axis=1) ** 0.5
            D_N = np.sum((temp2 ** 2), axis=1) ** 0.5
        else:
            print("默认所有指标权重相同")
            temp_max = np.max(self.array, axis=0)  # PIS
            temp_min = np.min(self.array, axis=0)  # NIS
            temp1 = self.array - np.tile(temp_max, (self.n, 1))
            temp2 = self.array - np.tile(temp_min, (self.n, 1))
            D_P = np.sum((temp1 ** 2), axis=1) ** 0.5
            D_N = np.sum((temp2 ** 2), axis=1) ** 0.5
        S = D_N / (D_P + D_N)  # 未归一化的得分
        assert S.shape[0] == self.n, "得分的方案个数不等于n"
        self.std_S = S / S.sum(axis=0)  # 将得分标准化
        return

if __name__ == "__main__":
    x_mat = np.array([[1,2,3,4],[1,2,3,4],[3,4,6,8],[5,2,7,2],[2,3,4,1]]) # 读取数据
    topsis_model = TOPSIS(x_mat) #利用np矩阵创建topsis模型
    topsis_model.data_positivization('micro',i=1)
    topsis_model.data_positivization('intermediate', i=2, reference1 = 3.0)
    topsis_model.normalization()
    topsis_model.cal_score([0.2,0.2,0.3,0.3])#需要输入AHP得到的权重作为参数
    print(f"每个案例的得分分别是{topsis_model.std_S}")